#include <unistd.h>
#include <sys/epoll.h>
#include <fcntl.h>
#include <cstring>

#include "ioscheduler.h"

static bool debug = true;

namespace zsylar
{

// 获取当前线程 IOManager 实例
IOManager* IOManager::GetThis()
{
    return dynamic_cast<IOManager*>(Scheduler::GetThis());
}

// 获取指定事件的上下文
IOManager::FdContext::EventContext& IOManager::FdContext::getEventContext(Event event)
{
    assert(event == READ || event == WRITE); // 确保事件是 READ 或 WRITE
    switch (event)
    {
    case READ:
        return read; // 返回读事件上下文
    case WRITE:
        return write; // 返回写事件上下文
    }
    throw std::invalid_argument("Unsupported event type"); // 不支持的事件类型
}

// 重置事件上下文
void IOManager::FdContext::resetEventContext(EventContext &ctx)
{
    ctx.scheduler = nullptr; // 清空调度器
    ctx.fiber.reset();       // 重置协程
    ctx.cb = nullptr;        // 清空回调函数
}

// 触发指定事件
void IOManager::FdContext::triggerEvent(IOManager::Event event)
{
    assert(events & event); // 确保事件存在

    // 删除事件：为了保证事件的唯一性
    events = (Event)(events & ~event);

    // 触发事件
    EventContext& ctx = getEventContext(event);
    if (ctx.cb)
    {
        // 调用调度器的 schedulelock 方法，调度回调函数
        ctx.scheduler->scheduleLock(&ctx.cb);
    }
    else
    {
        // 调用调度器的 schedulelock 方法，调度协程
        ctx.scheduler->scheduleLock(&ctx.fiber);
    }

    // 重置事件上下位
    resetEventContext(ctx);
    return;
}

// IOManager 构造函数，初始化线程池、定时器管理和epoll
IOManager::IOManager(size_t threads, bool use_caller, const std::string& name)
    : Scheduler(threads, use_caller, name), TimerManager()
{
    // 创建epoll文件描述符
    m_epfd = epoll_create(5000);
    assert(m_epfd > 0);

    // 创建管道文件描述符，用于唤醒调度器
    int rt = pipe(m_tickleFds);
    assert(!rt);

    // 将管道的读端添加到epoll中
    epoll_event event;
    event.events = EPOLLIN | EPOLLET; // 设置为边缘触发：事件发生是通知一次
    event.data.fd = m_tickleFds[0]; // 设置管道读端文件描述符

    // 设置非阻塞模式
    rt = fcntl(m_tickleFds[0], F_SETFL, O_NONBLOCK);
    assert(!rt);

    // 将管道读端添加到epoll中
    // 这样当管道有数据可读时，epoll会通知调度器
    // 这通常用于唤醒调度器以处理事件或任务
    // epoll_ctl 函数用于控制 epoll 实例的行为
    // EPOLL_CTL_ADD 表示添加一个新的文件描述符到 epoll 实例
    // m_tickleFds[0] 是管道的读端文件描述符
    // &event 是 epoll_event 结构体，包含事件类型和数据
    rt = epoll_ctl(m_epfd, EPOLL_CTL_ADD, m_tickleFds[0], &event);
    assert(!rt);

    // 初始化文件描述符上下文列表
    contextResize(32); // 初始大小为 32

    // 启动调度器
    start();
}

// 析构函数
IOManager::~IOManager()
{
    stop(); // 停止调度器
    close(m_epfd); // 关闭 epoll 文件描述符
    close(m_tickleFds[0]); // 关闭管道读端
    close(m_tickleFds[1]); // 关闭管道写端

    // 删除文件描述上下文
    for (size_t i = 0; i < m_fdContexts.size(); ++i)
    {
        if (m_fdContexts[i])
        {
            delete m_fdContexts[i]; // 释放每个文件描述符上下文
        }
    }
}

// 调整文件描述符上下文的大小
void IOManager::contextResize(size_t size)
{
    m_fdContexts.resize(size);

    for (size_t i = 0; i < m_fdContexts.size(); ++i)
    {
        if (m_fdContexts[i] == nullptr)
        {
            m_fdContexts[i] = new FdContext(); // 创建新的文件描述符上下文
            m_fdContexts[i]->fd = i;           // 设置文件描述符
        }
    }
}











}